Core.Storage.Dao.Complex = function(path, storage) {
	this.path = path;
	this.storage = storage || Core.Storage.Raw;
	
	this.paths = [];
	this.prevs = [];
	this.values = [];
	this.storages = [];
	this.observers = [];
	
	this.cacheStorages();
	this.cacheValues();
	
	var self = this;
	this.updateFn = function() { self.update(); }
	
	for(var i = 0, l = this.paths.length; i < l; i++) {
		this.storages[i].register(this.paths[i], this.updateFn);
	}
};

Core.Storage.Dao.Complex.prototype = {
	
	get: function() {
		return this.value;
	},
	
	update: function() {
		var set = [];
		
		this.cacheValues(set);
		this.observeValues(set);
		
		// Updating the observers
		for(var i = 0, l = this.observers.length; i < l; i++) {
			this.observers[i]();
		}
	},
	
	register: function(fn) {
		this.observers.push(fn);
	},
	
	unregister: function(fn) {
		this.observers.erase(fn);
	},
	
	cacheStorages: function(arr) {
		if(!arr) { arr = this.paths; }
		this.storages.push(arr[0] ? Core.Storage.Raw : this.storage);
		
		for(var i = 1, l = arr.length; i < l; i++) {
			if(typeof arr[i] !== "string") {
				this.cacheStorages(arr[i]);
			}
		}
	},
	
	cacheValues: function(set) {
		this.value = this._cacheValues(this.path, [], set);
	},
	
	_cacheValues: function(path, st, set) {
		var arr = path.slice(1),
			id = this.paths.length;
		
		// Evaluate the array path
		st.push(id);
		for(var i = 0, l = arr.length; i < l; i++) {
			if(typeof arr[i] !== "string") {
				arr[i] = this._cacheValues(arr[i], st, set);
			}
		}
		st.pop();
		
		// Fill set (if defined) for differential re-registering
		var value = this.storages[id].get(arr);
		if(set && this.values[id] !== value) {
			for(var i = 0, l = st.length; i < l; i++) {
				if(set.indexOf(st[i]) === -1) { set.push(st[i]); }
			}
		}
		
		// Cache the value
		this.paths[id] = arr;
		this.values[id] = value;
		
		return value;
	},
	
	/**
	 * @param Array set - array with changed paths' ids.
	 */
	observeValues: function(set) {
		var l = set.length, fn = this.updateFn;
		
		// Unregisters previous paths
		for(var i = 0; i < l; i++) {
			this.storages[set[i]].unregister(this.prevs[set[i]], fn);
		}
		
		// Register current paths
		for(var i = 0; i < l; i++) {
			this.storages[set[i]].register(this.paths[set[i]], fn);
		}
	}
};